﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.IO;
using System.IO.Pipes;
using System.Runtime.Serialization.Formatters.Binary;

namespace ProjetReseau
{
    class LiaisonDonnees // et simulation de B
    {
        public Dictionary<byte, byte[]> listeAdresse; // no connexion, [adresse source, adresse destinataire]
        private static Semaphore semLD;
        
        public LiaisonDonnees()
        {
            semLD = new Semaphore(1, 1);
            
            Thread thLireReseau = new Thread(this.lireDeReseau);
            thLireReseau.Start();

            listeAdresse = new Dictionary<byte, byte[]>();
        }

        public void lireDeReseau()
        {
            while (true)
            {
                try
                {
                    using (NamedPipeServerStream pipeServer =
                    new NamedPipeServerStream("restold", PipeDirection.In))
                    {
                        pipeServer.WaitForConnection();

                        BinaryFormatter bf = new BinaryFormatter();
                        Paquet paq = (Paquet)bf.Deserialize(pipeServer);


                        Thread thEcrire = new Thread(this.ecritureDeReseau);

                        thEcrire.Start(paq);

                    }
                }
                catch (IOException e)
                {

                }
            }
        }

        private void ecritureDeReseau(object _obj)
        {
            semLD.WaitOne();

            Paquet paq = (Paquet)_obj;
            byte[] paquet = paq.bytes;
            
            ecrireL_lec(paquet);
            traiterLecture(paquet);

            semLD.Release();
        }

        public void traiterLecture(byte[] octet)
        {
            byte[] paquet;
            switch (octet[1])
            {
                case 0x0B: // paquet d'appel (00001011)

                    listeAdresse.Add(octet[0], new byte[] { octet[2], octet[3] });

                    if (octet[2] % 19 != 0)
                    {
                        if (octet[2] % 13 == 0)
                        {
                            // paquet d'indication de liberation de la part du distant 00000001
                            paquet = new byte[5];

                            paquet[0] = octet[0];
                            paquet[1] = 0x13;
                            paquet[2] = octet[2];
                            paquet[3] = octet[3];
                            paquet[4] = 0x01;
                        }
                        else
                        {
                            // paquet de communication etablie 00001111
                            paquet = new byte[4];

                            paquet[0] = octet[0];
                            paquet[1] = 0x0F;
                            paquet[2] = octet[2];
                            paquet[3] = octet[3];
                        }

                        ecrireVersReseau(paquet);
                    }
                break;

                case 0x13: // paquet de demande de libération (00010011)
                    // pas certains qu'on doit faire un pacquet d'indication de liberation de connexion pour confirmer la fermeture...
                    /*paquet = new byte[5];

                    paquet[0] = octet[0];
                    paquet[1] = 0x13;
                    paquet[2] = octet[2];
                    paquet[3] = octet[3];
                    paquet[4] = 0x02; // indication de liberation de la part du fournissuer*/
                break;

                default: // paquet de data
                    byte adrSource = listeAdresse[octet[0]][0];

                    if (adrSource % 15 != 0)
                    {
                        byte pr = (byte)(octet[1] / (byte)32);
                        byte ps = (byte)((octet[1] % (byte)16) / (byte)2);

                        Random random = new Random();
                        byte rdmPs = (byte)random.Next(0, 7);

                        paquet = new byte[2];
                        paquet[0] = octet[0];

                        if (ps == rdmPs)
                        {
                            // acquitement negatif
                            paquet[1] = (byte)(ps * (byte)32 + (byte)9);
                        }
                        else
                        {
                            // acquitement positif
                            paquet[1] = (byte)(((ps + (byte)1) * (byte)32) + (byte)1);
                        }

                        ecrireVersReseau(paquet);
                    }
                break;
            }
        }

        // envoyer le paquet dans le pipe qui remonte vers la couche reseau
        public void ecrireVersReseau(byte[] _paq)
        {
            using (NamedPipeClientStream pipeClient =
            new NamedPipeClientStream(".", "ldtores", PipeDirection.Out))
            {
                bool retry = false;
                do
                {
                    try
                    {
                        while (!pipeClient.IsConnected)
                            pipeClient.Connect(500);
                    }
                    catch (IOException e)
                    {
                        retry = true;
                    }
                    catch (TimeoutException e)
                    {
                        retry = true;
                    }
                }
                while (retry);

                Paquet paq = new Paquet(_paq);

                BinaryFormatter bf = new BinaryFormatter();
                bf.Serialize(pipeClient, paq);
                ecrireL_ecr(_paq);

                //pipeClient.WaitForPipeDrain();
                pipeClient.Close();
                pipeClient.Dispose();
            }
        }

        // ecrire dans le fichier L.LEC
        public void ecrireL_lec(byte[] _paquet)
        {
            StreamWriter sw = File.AppendText("L_lec.txt");

            String octetStr = "";

            foreach (byte octet in _paquet)
            {
                octetStr += Convert.ToString(octet, 2).PadLeft(8, '0');
            }

            sw.WriteLine(octetStr);

            sw.Close();
            sw.Dispose();
        }

        // ecrire dans le fichier L.ECR les paquets provenant du pipe ER->LD
        public void ecrireL_ecr(byte[] _paquet)
        {
            StreamWriter sw = File.AppendText("L_ecr.txt");
            String octetStr = "";

            foreach (byte octet in _paquet)
            {
                octetStr += Convert.ToString(octet, 2).PadLeft(8, '0');
            }

            sw.WriteLine(octetStr);

            sw.Close();
            sw.Dispose();
        }

        // former un string avec le paquet pour ecrire dans L.LEC
        public string paquetToString(byte[] _paq)
        {
            string s = "(" + _paq[0] + ") ";

            foreach (byte b in _paq)
                s += b + ", ";
            
            return s.Substring(0, s.Length - 2);
        }
    }
}